using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

using ZedGraph;

using Pg.BioMedics.RatObserver.Media;
using Pg.BioMedics.RatObserver.ImageProcessing;

namespace Pg.BioMedics.RatObserver.MediaTester
{
    public partial class ExtractForm : Form
    {
        #region Delegates

        private delegate void VoidDelegate();
        private delegate void SetIntDelegate(int value);

        #endregion

        #region Fields

        private FrameBuffer _FrameBuffer;
        private ImageProcessor _ImageProcessor;

        #endregion

        #region Constructors

        public ExtractForm(FrameBuffer frameBuffer)
        {
            InitializeComponent();

            _FrameBuffer = frameBuffer;
            _FrameBuffer.OnChanged += new EventHandler(FrameBuffer_OnChanged);
            _FrameBuffer.OnStopActive += new EventHandler(FrameBuffer_OnStopActive);

            _ImageProcessor = new ImageProcessor();
        }        

        #endregion

        #region Event handlers

        void FrameBuffer_OnChanged(object sender, EventArgs e)
        {
            if (_FrameBuffer.Count > 0)
                previewPictureBox.Image = _ImageProcessor.Process(_FrameBuffer.Bitmaps[_FrameBuffer.Count - 1]);
            else
                previewPictureBox.Image = null;

            bufferFillProgressBar.Invoke(Delegate.CreateDelegate(typeof(SetIntDelegate), bufferFillProgressBar, "set_Value"), 
                                        (int)(((double)_FrameBuffer.Count / (double)_FrameBuffer.Capacity) * 100.0d));
            bufferFillProgressBar.Refresh();            
        }

        void FrameBuffer_OnStopActive(object sender, EventArgs e)
        {
            CalculateResults();
        }

        private void startButton_Click(object sender, EventArgs e)
        {
            try
            {
                _FrameBuffer.RunActive();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void stopButton_Click(object sender, EventArgs e)
        {
            try
            {
                _FrameBuffer.StopActive();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void clearButton_Click(object sender, EventArgs e)
        {
            try
            {
                _FrameBuffer.Clear();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        private void stepButton_Click(object sender, EventArgs e)
        {
            try
            {
                _FrameBuffer.RequestFrame();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }

        #endregion     
   
        #region Support methods

        private void CalculateResults()
        {
            try
            {
                GraphPane graphPane = zedGraph.GraphPane;

                graphPane.Title.Text = "Motion levels [%]";
                graphPane.XAxis.Title.Text = "Frame number";
                graphPane.YAxis.Title.Text = "Level [%]";

                graphPane.CurveList.Clear();
                PointPairList points = new PointPairList();
                PointPairList filteredPoints = new PointPairList();

                double[] levels = _ImageProcessor.MotionLevels;
                double[] filteredLevels = FilterLevels(levels);
                for (int i = 0; i < levels.Length; i++)
                {
                    points.Add((double)i, levels[i]);
                    filteredPoints.Add((double)i, filteredLevels[i]);
                }
                graphPane.AddCurve("Levels", points, Color.Red, SymbolType.None);
                graphPane.AddCurve("Filtered Levels", filteredPoints, Color.Blue, SymbolType.None);

                zedGraph.AxisChange();
                BeginInvoke(new VoidDelegate(delegate() { Refresh(); }));
                BeginInvoke(new VoidDelegate(delegate()
                {
                    rotationsTextBox.Text = CountRotations(filteredLevels).ToString() + " CW";
                }));
            }
            catch (Exception ex)
            {
                MessageBox.Show("Can't calculate results");
            }
        }

        private double[] FilterLevels(double[] levels)
        {
            MovingAverageCalculator maCalc = new MovingAverageCalculator(20);
            MovingAverageCalculator maCalc2 = new MovingAverageCalculator(5);
            MovingAverageCalculator maCalc3 = new MovingAverageCalculator(10);            

            double[] tmpResult = new double[levels.Length];
            for (int i = 0; i < levels.Length; i++)
            {
                tmpResult[i] = maCalc.NextValue(levels[i]);
            }

            double[] tmpResult2 = new double[levels.Length];
            for (int i = 0; i < levels.Length; i++)
            {
                tmpResult2[i] = maCalc2.NextValue(tmpResult[i]);
            }

            double[] result = new double[levels.Length];
            for (int i = 0; i < levels.Length; i++)
            {
                result[i] = maCalc3.NextValue(tmpResult[i]);
            }

            return result;
        }

        private int CountRotations(double[] values)
        {
            double[] diff = Diff(values);

            int result = 0;
            double lastMax = 0;
            double lastMin = 0;
            for (int i = 1; i < diff.Length - 1; i++)
            {
                if ((diff[i - 1] > 0) && (diff[i] < 0))
                {
                    lastMax = values[i];

                    if (lastMin < (lastMax - 0.35 * lastMax))
                        result++;
                }
                else if ((diff[i - 1] < 0) && (diff[i] > 0))
                {
                    lastMin = values[i];
                }
            }

            return result;
        }

        private double[] Diff(double[] values)
        {
            double[] result = new double[values.Length];
            for (int i = 1; i < result.Length - 1; i++)
            {
                result[i] = (values[i + 1] - values[i - 1]) / 2;
            }

            return result;
        }

        #endregion
    }
}